package com.wzw.tmaceventbus.eventbus;

import android.os.Handler;
import android.os.Looper;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * @author : wangzw
 * @date : 21-1-7下午4:14
 * @desc :
 */
public class TmacEventBus {
    private static final int BRIDGE = 0x40;
    private static final int SYNTHETIC = 0x1000;
    private static volatile TmacEventBus instance;
    private Map<Object, List<TmacSubscribeMethod>> cacheMap;
    private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
    private ExecutorService executorService;
    private Handler handler;

    public static TmacEventBus getDefault() {
        if (instance == null) {
            synchronized (TmacEventBus.class) {
                if (instance == null) {
                    instance = new TmacEventBus();
                }
            }
        }
        return instance;
    }


    public TmacEventBus() {
        cacheMap = new HashMap<>();
        executorService = Executors.newFixedThreadPool(3);
        handler = new Handler(Looper.myLooper());
    }

    /**
     * 注册
     *
     * @param subscriber 注册者 通常是fragment activity
     */
    public void register(Object subscriber) {
        //未注册则创建添加到缓存，反之不添加防止多次创建造成消耗
        List<TmacSubscribeMethod> tmacSubscribeMethods = cacheMap.get(subscriber);
        if (tmacSubscribeMethods == null) {
            tmacSubscribeMethods = getSubScribeMethods(subscriber);
            cacheMap.put(subscriber, tmacSubscribeMethods);
        }

    }

    private List<TmacSubscribeMethod> getSubScribeMethods(Object subscriber) {
        //获取到订阅者类
        Class<?> aClass = subscriber.getClass();
        List<TmacSubscribeMethod> list = new ArrayList<>();
        while (aClass != null) {
            //如果是系统的类直接忽略
            String name = aClass.getName();
            if (name.startsWith("java.") ||
                    name.startsWith("javax.") ||
                    name.startsWith("android.") ||
                    name.startsWith("androidx.")) {
                break;
            }

            //获取到所有方法
            Method[] declaredMethods = aClass.getDeclaredMethods();
            if (declaredMethods != null) {
                for (Method declaredMethod : declaredMethods) {
                    int modifiers = declaredMethod.getModifiers();
                    if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                        //获取每个方法上面的特定注解
                        TmacSubscribe annotation = declaredMethod.getAnnotation(TmacSubscribe.class);
                        if (annotation == null) {
                            continue;
                        }
                        //参数列表
                        Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
                        if (parameterTypes.length != 1) {
                            throw new TmacEventBusException("@Subscribe method " + declaredMethod +
                                    "must have exactly 1 parameter but has " + parameterTypes.length);
                        }

                        if (annotation != null) {
                            //获取threadMode
                            TmacSubscribeThreadMode tmacSubscribeThreadMode = annotation.THREAD_MODE();
                            list.add(new TmacSubscribeMethod(declaredMethod, parameterTypes[0], tmacSubscribeThreadMode));

                        }
                    }else if(declaredMethod.isAnnotationPresent(TmacSubscribe.class)){
                        String methodName = declaredMethod.getDeclaringClass().getName() + "." + declaredMethod.getName();
                        throw new TmacEventBusException(methodName +
                                " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
                    }


                }
            }

            //一层一层判断
            aClass = aClass.getSuperclass();
        }

        return list;

    }


    /**
     * 反注册
     *
     * @param subscriber
     */
    public void unRegister(Object subscriber) {
        List<TmacSubscribeMethod> list = cacheMap.get(subscriber);
        if (list != null) {
            cacheMap.remove(subscriber);
        }
    }


    /**
     * 发送
     *
     * @param event 事件类型
     */
    public void post(Object event) {
        Set<Object> subscribeSet = cacheMap.keySet();
        Iterator<Object> subscribeIterator = subscribeSet.iterator();

        while (subscribeIterator.hasNext()) {
            //获取订阅者
            Object subscriber = subscribeIterator.next();
            List<TmacSubscribeMethod> list = cacheMap.get(subscriber);
            for (TmacSubscribeMethod tmacSubscribeMethod : list) {
                if (tmacSubscribeMethod.getEventType().isAssignableFrom(event.getClass())) {
                    switch (tmacSubscribeMethod.getTmacThreadMode()) {
                        case MAIN:
                            //订阅者和发布者都在主线程
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                invoke(tmacSubscribeMethod.getMethod(), subscriber, event);
                            } else {
                                //发布者异步线程发布，接收者在主线程，利用handler切换到主线程
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(tmacSubscribeMethod.getMethod(), subscriber, event);
                                    }
                                });

                            }
                            return;
                        case ASYNC:
                            // 发布者在主线程发布， 订阅者在异步线程
                            if (Looper.myLooper() == Looper.getMainLooper()) {
                                executorService.submit(new Runnable() {
                                    @Override
                                    public void run() {
                                        invoke(tmacSubscribeMethod.getMethod(), subscriber, event);
                                    }
                                });
                            } else {
                                //发布者和订阅者都在异步线程
                                invoke(tmacSubscribeMethod.getMethod(), subscriber, event);

                            }

                            break;
                    }
                }
            }


        }
    }

    private void invoke(Method method, Object subscriber, Object params) {
        try {
            method.invoke(subscriber, params);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}
